/**                   GNU GENERAL PUBLIC LICENSE
 *                       Version 3, 29 June 2007
 *
 * Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 * Everyone is permitted to copy and distribute verbatim copies
 * of this license document, but changing it is not allowed.
 * See License.txt file that comes with this distribution
 *
 *
 * Author: Vitaly German <vitaly.german@gmail.com>, (C) 2007-2008
 * 
 **/

#pragma once
#include <iostream>
#include <map>
#include <boost/shared_ptr.hpp>

/// Requires:
/// 1. Node IDs (grammar)


class Result;

typedef boost::shared_ptr<Result> Result_aptr;
typedef Result * Result_ptr;

class Result {
    public:
    virtual ~Result(){}

    static Result_aptr create(const Numeric & n);
template <class Node>
    static Result_aptr create(const Node & n, int sign =1);
    
    
};


class NumericResult:public Result {
    Numeric value_;
public:

    NumericResult(const Numeric & val = Numeric(0)):value_(val){}
    operator Numeric () const {return value_;}
    template <class Fun>
    void update_const(const Numeric & c, Fun f ){
        value_ = f(value_, c);
    }
};


class VarResult:public Result {
public:
typedef std::map<std::string, Numeric, std::less<std::string> > VarTable;
private:
    VarTable coefs_;
    Numeric const_;
public:

    /// no variables but constant
    VarResult(Numeric val = Numeric(0)):const_(val){}
    /// no constant but variable with coefficient
    VarResult(const std::string & var, Numeric val = Numeric(0)):const_(0){
	coefs_[var] = val;
    }
    
    template <class Fun>
    void update_const(const Numeric c, Fun f ){
        const_ = f(const_, c);
    }
   
    Numeric const_val() const{
	return  const_;
    }

    const VarTable & var_val()const{
        return coefs_;
    }

    template <class Fun>
    void merge_result(const VarResult & o, Fun f ){
        typedef typename VarResult::VarTable::const_iterator CIT;
	const typename VarResult::VarTable & vars = o.var_val();
        for (CIT it = vars.begin(); it!=vars.end(); ++it){
            if (coefs_.find(it->first)==coefs_.end()){
                coefs_[it->first] = Numeric(0);
            }
            coefs_[it->first] = f(coefs_[it->first], it->second);
        }
        update_const(o.const_val(), f);
    }

    template <class Fun>
    void mult_result(const Numeric & o, Fun f ){
        typedef typename VarTable::iterator IT;
        for (IT it = coefs_.begin(); it!=coefs_.end(); ++it){
            it->second = f( it->second, o);
        }
        const_ = f(const_, o);
    }

};

///
/// yes factory mast know about all particular types but we have only two ... so why bother
///

template <class Node>
Result_aptr Result::create(const Node & node, int sign){
    /// if node is numeric create numeric node
    if (node.value.id() == calculator::realID){
        return Result_aptr (new NumericResult(sign * DoubleFromNode(node)));
    }
    assert(node.value.id() == calculator::variableID);
    std::string sv(node.value.begin(), node.value.end());
    return Result_aptr (new VarResult(sv, sign));
};

Result_aptr Result::create(const Numeric & n){
    return Result_aptr (new NumericResult(n));
}

typedef boost::shared_ptr<VarResult> VarResult_aptr;
typedef boost::shared_ptr<NumericResult> NumericResult_aptr;
